import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.TreeMap;

public class HypergeometricDistributionCalculator {
	private int var_N;
	private int var_n;
	private int var_k;
	private int var_fromX;
	private int var_toX;

	// Constructor
	public HypergeometricDistributionCalculator(int var_fromX, int var_toX,
			int var_N, int var_n, int var_k) {
		this.var_N = var_N;
		this.var_n = var_n;
		this.var_k = var_k;
		this.var_fromX = var_fromX;
		this.var_toX = var_toX;
	}

	private BigDecimal combination(int n, int r) {
		BigDecimal ans = BigDecimal.ONE;
		for (long i = n; i > n - r; i--) {
			ans = ans.multiply(BigDecimal.valueOf(i));
		}
		for (long i = 1; i <= r; i++) {
			ans = ans.divide(BigDecimal.valueOf(i));
		}
		return ans;
	}

	// Hypergeometric Distribution //////////////////////////////////

	private double getProbabilityOfX(int var_x, int var_N, int var_n, int var_k) {
		// Returns 0 if number of successes in sample is greater than sample
		// size (this is impossible)
		if (var_n < var_x) {
			return 0;
		}
		BigDecimal top = combination(var_k, var_x).multiply(
				combination((var_N - var_k), (var_n - var_x)));
		BigDecimal bottom = combination(var_N, var_n);
		BigDecimal answer = top.divide(bottom, 10, RoundingMode.HALF_UP);

		double ans = answer.doubleValue();

		if (ans > 1) {
			return 1;
		} else {
			return ans;
		}
	}

	// Gets a Map of all the probabilities of all x's in a given list of x's
	// (Result -> <Value of X, Probability of X>)
	private Map<Integer, Double> getProbabilityListOfX(int var_fromX,
			int var_toX, int var_N, int var_n, int var_k) {
		Map<Integer, Double> probabilityList = new TreeMap<Integer, Double>();
		for (int i = var_fromX; i <= var_toX; i++) {
			probabilityList.put(i, getProbabilityOfX(i, var_N, var_n, var_k));
		}
		return probabilityList;
	}

	public Map<Integer, Double> getProbabilityListOfX() {
		return getProbabilityListOfX(var_fromX, var_toX, var_N, var_n, var_k);
	}

	// Simulation ///////////////////////////////////////////

	// Simulates the probability list as n goes up
	public Map<Integer, Map<Integer, Double>> simulate_n(int var_fromX,
			int var_toX, int var_N, int var_n, int var_k) {
		Map<Integer, Map<Integer, Double>> n_simulation = new TreeMap<Integer, Map<Integer, Double>>();
		for (int n = 0; n <= var_N; n++) {
			n_simulation.put(n,
					getProbabilityListOfX(var_fromX, var_toX, var_N, n, var_k));
		}
		return n_simulation;
	}

	// Simulates the probability list as k goes up
	public Map<Integer, Map<Integer, Double>> simulate_k(int var_fromX,
			int var_toX, int var_N, int var_n, int var_k) {
		Map<Integer, Map<Integer, Double>> k_simulation = new TreeMap<Integer, Map<Integer, Double>>();
		for (int k = 0; k <= var_N; k++) {
			k_simulation.put(k,
					getProbabilityListOfX(var_fromX, var_toX, var_N, var_n, k));
		}
		return k_simulation;
	}

	// Getters{
	public int getVar_N() {
		return this.var_N;
	}

	public int getVar_n() {
		return this.var_n;
	}

	public int getVar_k() {
		return this.var_k;
	}

	public int getVar_fromX() {
		return this.var_fromX;
	}

	public int getVar_toX() {
		return this.var_toX;
	}
}
